package com.botarena.server.services;

import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.List;

import javax.jdo.PersistenceManager;

import com.botarena.server.dao.DAO;
import com.botarena.server.dao.filter.Filter;
import com.botarena.server.dao.helper.NonTransactionalReturn;
import com.botarena.server.model.Entity;
import com.botarena.server.services.validation.Validator;
import com.google.appengine.api.datastore.Key;

public abstract class GenericService<E extends Entity, D extends DAO<E>, V extends Validator<E>> implements Service<E> {
	
	private final Class<E> persistentClass;
	private final Class<D> daoClass;
	private final Class<V> validationClass;
	
	protected Validator<E> validator;
	
	@SuppressWarnings("unchecked")
	public GenericService() {
		persistentClass = (Class<E>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
		daoClass = (Class<D>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[1];
		validationClass = (Class<V>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[2];
		
		createValidator();
	}
	
	
	private void createValidator() {
		try {
			validator = validationClass.newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public Class<E> getPersistentClass() {
		return persistentClass;
	}
	
	public Class<D> getDAOClass() {
		return daoClass;
	}
	
	public Class<V> getValidatorClass() {
		return validationClass;
	}
	
	private D getDAOInstance(PersistenceManager pm) {
		D dao = null;
		try {
			dao = (D) getDAOClass().getConstructor(PersistenceManager.class).newInstance(pm);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return dao;
	}
	
	// IMPORTANT
	// The following methods should not be invoked inside other methods of a 
	// GenericService concrete classes. Methods of DAO should be used instead.
	// These methods wrap up GenericDAO CRUD methods to expose them in service.
	
	@Override
	public List<E> getAll() {
		return DAOService.execute(new NonTransactionalReturn<List<E>>() {
			
			@Override
			public List<E> doOutsideTransaction(PersistenceManager pm) {
				return getDAOInstance(pm).getAll();
			}
		});
	}
	
	@Override
	public List<E> getAll(final Filter<E> filter) {
		return DAOService.execute(new NonTransactionalReturn<List<E>>() {
			
			@Override
			public List<E> doOutsideTransaction(PersistenceManager pm) {
				return getDAOInstance(pm).getAll(filter);
			}
		});
	}
	
	@Override
	public E getByKey(final Key key) {
		return DAOService.execute(new NonTransactionalReturn<E>() {
			
			@Override
			public E doOutsideTransaction(PersistenceManager pm) {
				return getDAOInstance(pm).getByKey(key);
			}
		});
	}
	
	@Override
	public E getByKey(final String key) {
		return DAOService.execute(new NonTransactionalReturn<E>() {
			
			@Override
			public E doOutsideTransaction(PersistenceManager pm) {
				return getDAOInstance(pm).getByKey(key);
			}
		});
	}
	
	@Override
	public Collection<E> getByKeys(final Collection<Key> keys) {
		return DAOService.execute(new NonTransactionalReturn<Collection<E>>() {
			
			@Override
			public Collection<E> doOutsideTransaction(PersistenceManager pm) {
				return getDAOInstance(pm).getByKeys(keys);
			}
		});
	}
}
